
/*
    Software License

    Copyright (C) 2021-05-24  Xoronos

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, version 3.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

/*
    Liabilities

    The software is provided "AS IS" without any warranty of any kind, either expressed,
    implied, or statutory, including, but not limited to, any warranty that the software
    will conform to specifications, any implied warranties of merchantability, fitness
    for a particular purpose, and freedom from infringement, and any warranty that the
    documentation will conform to the software, or any warranty that the software will
    be error free.

    In no event shall Xoronos be liable for any damages, including, but not limited to,
    direct, indirect, special or consequential damages, arising out of, resulting from,
    or in any way connected with this software, whether or not based upon warranty,
    contract, tort, or otherwise, whether or not injury was sustained by persons or
    property or otherwise, and whether or not loss was sustained from, or arose out of
    the results of, or use of, the software or services provided hereunder.

    To request the provided software under a different license you can contact us at
    support@xoronos.com
*/

#include "xrn_common.h"
#include "xrn_common_backend.h"

xrn_log_t xlog;

int xrn_make_checksums_wrapper( FILE * fpi, FILE * fpo
 )
{
    XPRINT_DEB(  );

    uint64_t read_bytes;
    uint64_t written_bytes;

    cksumnum_t xorchsumnum;
    cksumnum_t chsumnum_init_buff[XRN_CHKSUM_BUFF];
    cksumnum_t chsumnum_buff[XRN_CHKSUM_BUFF];

    uint8_t done_read, xcrc8;
    uint8_t buff_index;
    uint8_t extra_read_needed;
    uint64_t i, j;
    int eof;

    if ( ( fpi == NULL ) || ( fpo == NULL ) ) {
        XPRINT_ERR( 752, XERR_NULL_MSG );
        return XERR_NULL;
    }

    for ( i = 0; i < 32; i++ ) {
        xorchsumnum.num[i] = 0;
    }

    done_read = 0;
    extra_read_needed = 0;
    buff_index = 0;

    // read the initial buffer

    read_bytes = fread( chsumnum_init_buff, 1, sizeof( chsumnum_init_buff ), fpi );
    eof = xrn_feof( fpi );

    if ( ( eof == 1 ) && ( read_bytes == sizeof( chsumnum_init_buff ) ) ) {
        extra_read_needed = 1;
    } else if ( read_bytes < sizeof( chsumnum_init_buff ) ) {
        for ( j = read_bytes; j < sizeof( chsumnum_init_buff ); j++ ) {
            if ( j == read_bytes ) {
                ( ( uint8_t * ) chsumnum_init_buff )[j] = 0x80;
            } else {
                ( ( uint8_t * ) chsumnum_init_buff )[j] = 0x00;
            }

        }

        done_read = 1;
    }

    // copy initial buffer and compute the initial xor checksum

    for ( i = 0; i < XRN_CHKSUM_BUFF; i++ ) {

        chsumnum_buff[i] = chsumnum_init_buff[i];

        for ( j = 0; j < 32; j++ ) {
            xorchsumnum.num[j] = xorchsumnum.num[j] ^ chsumnum_buff[i].num[j];
        }

    }

    xcrc8 = cksumnum_buff_crc8( buff_index, chsumnum_buff );
    written_bytes = fwrite( &xcrc8, 1, sizeof( xcrc8 ), fpo );
    buff_index = ( uint8_t ) ( ( buff_index + 1 ) % XRN_CHKSUM_BUFF );

    if ( written_bytes != sizeof( xcrc8 ) ) {

        XPRINT_ERR( 736, XERR_IO_MSG );
        return XERR_IO;

    }

    while ( done_read != 1 ) {

        // read one chsumnum

        read_bytes = fread( &chsumnum_buff[buff_index], 1, sizeof( chsumnum_buff[buff_index] ), fpi );
        eof = xrn_feof( fpi );

        if ( extra_read_needed == 1 ) {

            for ( j = 0; j < 32; j++ ) {

                if ( j == 0 ) {

                    chsumnum_buff[buff_index].num[j] = 0x80;

                } else {

                    chsumnum_buff[buff_index].num[j] = 0x00;

                }

            }

            done_read = 1;

        } else if ( ( eof == 1 ) && ( read_bytes == sizeof( chsumnum_buff[buff_index] ) ) ) {

            extra_read_needed = 1;

        } else if ( eof == 1 ) {

            for ( j = read_bytes; j < sizeof( chsumnum_buff[buff_index] ); j++ ) {

                if ( j == read_bytes ) {

                    chsumnum_buff[buff_index].num[j] = 0x80;

                } else {

                    chsumnum_buff[buff_index].num[j] = 0x00;

                }

            }

            done_read = 1;

        }

        // compute 256 bit xor checksum

        for ( j = 0; j < XRN_CHKSUM_BUFF; j++ ) {
            xorchsumnum.num[j] = xorchsumnum.num[j] ^ chsumnum_buff[buff_index].num[j];
        }

        // compute one output 8 bit crc

        xcrc8 = cksumnum_buff_crc8( buff_index, chsumnum_buff );
        written_bytes = fwrite( &xcrc8, 1, sizeof( xcrc8 ), fpo );

        if ( written_bytes != sizeof( xcrc8 ) ) {

            XPRINT_ERR( 739, XERR_IO_MSG );
            return XERR_IO;

        }

        buff_index = ( uint8_t ) ( ( buff_index + 1 ) % XRN_CHKSUM_BUFF );

    }

    // final copy the initial buffer into the buffer

    for ( i = 0; i < XRN_CHKSUM_BUFF - 1; i++ ) {

        for ( j = 0; j < 32; j++ ) {
            chsumnum_buff[buff_index].num[j] = chsumnum_init_buff[i].num[j];
        }

        // compute one output 8 bit crc
        xcrc8 = cksumnum_buff_crc8( buff_index, chsumnum_buff );

        written_bytes = fwrite( &xcrc8, 1, sizeof( xcrc8 ), fpo );

        if ( written_bytes != sizeof( xcrc8 ) ) {

            XPRINT_ERR( 754, XERR_IO_MSG );
            return XERR_IO;

        }

        buff_index = ( uint8_t ) ( ( buff_index + 1 ) % XRN_CHKSUM_BUFF );

    }

    written_bytes = fwrite( &xorchsumnum, 1, sizeof( xorchsumnum ), fpo );

    if ( written_bytes != sizeof( xorchsumnum ) ) {

        XPRINT_ERR( 755, XERR_IO_MSG );
        return XERR_IO;

    }

    return XSUCCESS;

}

int xrn_check_checksums_wrapper( FILE * fpi, FILE * fck
 )
{
    XPRINT_DEB(  );

    uint64_t read_bytes;
    uint64_t written_bytes;

    cksumnum_t xorchsumnum, xorchsumnum_read;
    cksumnum_t chsumnum_init_buff[XRN_CHKSUM_BUFF];
    cksumnum_t chsumnum_buff[XRN_CHKSUM_BUFF];

    uint8_t done_read, xcrc8, xcrc8_read;
    uint8_t buff_index;
    uint8_t extra_read_needed;
    uint64_t i, j;
    int eof;

    if ( ( fpi == NULL ) || ( fck == NULL ) ) {
        XPRINT_ERR( 760, XERR_NULL_MSG );
        return XERR_NULL;
    }

    for ( i = 0; i < XRN_CHKSUM_BUFF; i++ ) {
        xorchsumnum.num[i] = 0;
    }

    done_read = 0;
    extra_read_needed = 0;
    buff_index = 0;

    // read the initial buffer

    read_bytes = fread( chsumnum_init_buff, 1, sizeof( chsumnum_init_buff ), fpi );
    eof = xrn_feof( fpi );

    if ( ( eof == 1 ) && ( read_bytes == sizeof( chsumnum_init_buff ) ) ) {
        extra_read_needed = 1;
    } else if ( eof == 1 ) {
        for ( j = read_bytes; j < sizeof( chsumnum_init_buff ); j++ ) {
            if ( j == read_bytes ) {
                ( ( uint8_t * ) chsumnum_init_buff )[j] = 0x80;
            } else {
                ( ( uint8_t * ) chsumnum_init_buff )[j] = 0x00;
            }

        }

        done_read = 1;
    }

    // copy initial buffer and compute the initial xor checksum

    for ( i = 0; i < XRN_CHKSUM_BUFF; i++ ) {

        chsumnum_buff[i] = chsumnum_init_buff[i];

        for ( j = 0; j < XRN_CHKSUM_BUFF; j++ ) {
            xorchsumnum.num[j] = xorchsumnum.num[j] ^ chsumnum_buff[i].num[j];
        }

    }

    xcrc8 = cksumnum_buff_crc8( buff_index, chsumnum_buff );
    written_bytes = fread( &xcrc8_read, 1, sizeof( xcrc8 ), fck );
    buff_index = ( uint8_t ) ( ( buff_index + 1 ) % XRN_CHKSUM_BUFF );

    if ( written_bytes != sizeof( xcrc8 ) ) {

        XPRINT_ERR( 756, XERR_IO_MSG );
        return XERR_IO;

    }

    if ( xcrc8_read != xcrc8 ) {
        XPRINT_NOT( 1 );
        return XERR_CRC;
    }

    while ( done_read != 1 ) {

        // read one chsumnum
        read_bytes = fread( &chsumnum_buff[buff_index], 1, sizeof( chsumnum_buff[buff_index] ), fpi );
        eof = xrn_feof( fpi );

        if ( extra_read_needed == 1 ) {

            for ( j = 0; j < sizeof( chsumnum_buff[buff_index] ); j++ ) {

                if ( j == 0 ) {

                    ( ( uint8_t * ) & chsumnum_buff[buff_index] )[j] = 0x80;

                } else {

                    ( ( uint8_t * ) & chsumnum_buff[buff_index] )[j] = 0x00;

                }

            }

            done_read = 1;

        } else if ( ( eof == 1 ) && ( read_bytes == sizeof( chsumnum_buff[buff_index] ) ) ) {

            extra_read_needed = 1;

        } else if ( eof == 1 ) {

            for ( j = read_bytes; j < sizeof( chsumnum_buff[buff_index] ); j++ ) {

                if ( j == read_bytes ) {

                    ( ( uint8_t * ) & chsumnum_buff[buff_index] )[j] = 0x80;

                } else {

                    ( ( uint8_t * ) & chsumnum_buff[buff_index] )[j] = 0x00;

                }

            }

            done_read = 1;

        }

        // compute 256 bit xor checksum

        for ( j = 0; j < XRN_CHKSUM_BUFF; j++ ) {
            xorchsumnum.num[j] = xorchsumnum.num[j] ^ chsumnum_buff[buff_index].num[j];
        }

        // compute one output 8 bit crc

        xcrc8 = cksumnum_buff_crc8( buff_index, chsumnum_buff );
        written_bytes = fread( &xcrc8_read, 1, sizeof( xcrc8 ), fck );
        buff_index = ( uint8_t ) ( ( buff_index + 1 ) % XRN_CHKSUM_BUFF );

        if ( written_bytes != sizeof( xcrc8 ) ) {

            XPRINT_ERR( 757, XERR_IO_MSG );
            return XERR_IO;

        }

        if ( xcrc8_read != xcrc8 ) {
            XPRINT_NOT( 1 );
            return XERR_CRC;
        }

    }

    // final copy the initial buffer into the buffer

    for ( i = 0; i < XRN_CHKSUM_BUFF - 1; i++ ) {

        chsumnum_buff[buff_index] = chsumnum_init_buff[i];

        // compute one output 8 bit crc
        xcrc8 = cksumnum_buff_crc8( buff_index, chsumnum_buff );
        written_bytes = fread( &xcrc8_read, 1, sizeof( xcrc8 ), fck );
        buff_index = ( uint8_t ) ( ( buff_index + 1 ) % XRN_CHKSUM_BUFF );

        if ( written_bytes != sizeof( xcrc8 ) ) {

            XPRINT_ERR( 758, XERR_IO_MSG );
            return XERR_IO;

        }

        if ( xcrc8_read != xcrc8 ) {
            XPRINT_NOT( 1 );
            return XERR_CRC;
        }

    }

    written_bytes = fread( &xorchsumnum_read, 1, sizeof( xorchsumnum ), fck );

    if ( written_bytes != sizeof( xorchsumnum ) ) {

        XPRINT_ERR( 759, XERR_IO_MSG );
        return XERR_IO;

    }

    for ( i = 0; i < 32; i++ ) {
        if ( xorchsumnum_read.num[i] != xorchsumnum.num[i] ) {
            XPRINT_NOT( 1 );
            return XERR_CRC;
        }

    }

    XPRINT_NOT( 0 );
    return XSUCCESS;

}

int xrn_compare_version( uint32_t version, uint32_t git_ref
 )
{
    XPRINT_DEB(  );

    int final_ret;

    final_ret = XSUCCESS;

    if ( version != XRN_LIB_VERSION ) {
        XPRINT_ERR( 2252, XERR_VERSION_MSG );
        return XERR_VERSION;
    }

    if ( git_ref > XRN_GIT_VERSION ) {
        final_ret = xrn_store_warnings_in_return_value( ( int ) XWAR_VERSION, final_ret );
        XPRINT_WAR( XWAR_VERSION_MSG, XWAR_VERSION );
    }

    return final_ret;

}

void xrn_print_all_warnings( uint32_t current_return
 )
{
    XPRINT_DEB(  );

    if ( ( current_return & ( uint32_t ) XWAR_OPCODE ) == XWAR_OPCODE ) {

        current_return = ( current_return & ( ~XWAR_OPCODE ) );

        if ( ( current_return & ( uint32_t ) XWAR_TIMING ) == XWAR_TIMING ) {
            if ( ( xlog.war_shown & XWAR_TIMING ) != XWAR_TIMING ) {
                XPRINT_WAR( XWAR_TIMING_MSG, XWAR_TIMING );
                xlog.war_shown = xlog.war_shown | XWAR_TIMING;
            }

        }

        current_return = ( current_return & ( ~XWAR_TIMING ) );

        if ( ( current_return & ( uint32_t ) XWAR_FORMAT ) == XWAR_FORMAT ) {
            if ( ( xlog.war_shown & XWAR_FORMAT ) != XWAR_FORMAT ) {
                XPRINT_WAR( XWAR_FORMAT_MSG, XWAR_FORMAT );
                xlog.war_shown = xlog.war_shown | XWAR_FORMAT;
            }

        }

        current_return = ( current_return & ( ~XWAR_FORMAT ) );

        if ( ( current_return & ( uint32_t ) XWAR_SIG ) == XWAR_SIG ) {
            if ( ( xlog.war_shown & XWAR_SIG ) != XWAR_SIG ) {
                XPRINT_WAR( XWAR_SIG_MSG, XWAR_SIG );
                xlog.war_shown = xlog.war_shown | XWAR_SIG;
            }

        }

        current_return = ( current_return & ( ~XWAR_SIG ) );

        if ( ( current_return & ( uint32_t ) XWAR_VERSION ) == XWAR_VERSION ) {
            if ( ( xlog.war_shown & XWAR_VERSION ) != XWAR_VERSION ) {
                XPRINT_WAR( XWAR_VERSION_MSG, XWAR_VERSION );
                xlog.war_shown = xlog.war_shown | XWAR_VERSION;
            }

        }

        current_return = ( current_return & ( ~XWAR_VERSION ) );

        if ( ( current_return & ( uint32_t ) XWAR_TRY ) == XWAR_TRY ) {
            if ( ( xlog.war_shown & XWAR_TRY ) != XWAR_TRY ) {
                XPRINT_WAR( XWAR_TRY_MSG, XWAR_TRY );
                xlog.war_shown = xlog.war_shown | XWAR_TRY;
            }

        }

        current_return = ( current_return & ( ~XWAR_TRY ) );

        if ( ( current_return & ( uint32_t ) XWAR_PROB_ENC_C0 ) == XWAR_PROB_ENC_C0 ) {
            if ( ( xlog.war_shown & XWAR_PROB_ENC_C0 ) != XWAR_PROB_ENC_C0 ) {
                XPRINT_WAR( XWAR_PROB_ENC_C0_MSG, XWAR_PROB_ENC_C0 );
                xlog.war_shown = xlog.war_shown | XWAR_PROB_ENC_C0;
            }

        }

        current_return = ( current_return & ( ~XWAR_PROB_ENC_C0 ) );

        if ( ( current_return & ( uint32_t ) XWAR_PROB_ENC_C1 ) == XWAR_PROB_ENC_C1 ) {
            if ( ( xlog.war_shown & XWAR_PROB_ENC_C1 ) != XWAR_PROB_ENC_C1 ) {
                XPRINT_WAR( XWAR_PROB_ENC_C1_MSG, XWAR_PROB_ENC_C1 );
                xlog.war_shown = xlog.war_shown | XWAR_PROB_ENC_C1;
            }

        }

        current_return = ( current_return & ( ~XWAR_PROB_ENC_C1 ) );

        if ( ( current_return & ( uint32_t ) XWAR_PROB_ENC_0 ) == XWAR_PROB_ENC_0 ) {
            if ( ( xlog.war_shown & XWAR_PROB_ENC_0 ) != XWAR_PROB_ENC_0 ) {
                XPRINT_WAR( XWAR_PROB_ENC_0_MSG, XWAR_PROB_ENC_0 );
                xlog.war_shown = xlog.war_shown | XWAR_PROB_ENC_0;
            }

        }

        current_return = ( current_return & ( ~XWAR_PROB_ENC_0 ) );

        if ( ( current_return & ( uint32_t ) XWAR_PROB_ENC_1 ) == XWAR_PROB_ENC_1 ) {
            if ( ( xlog.war_shown & XWAR_PROB_ENC_1 ) != XWAR_PROB_ENC_1 ) {
                XPRINT_WAR( XWAR_PROB_ENC_C1_MSG, XWAR_PROB_ENC_C1 );
                xlog.war_shown = xlog.war_shown | XWAR_PROB_ENC_1;
            }

        }

        // current_return = ( current_return & ( ~XWAR_PROB_ENC_C1 ) );

    }

}

int xrn_store_warnings_in_return_value( int current_return, int previous_return
 )
{
    XPRINT_DEB(  );

    if ( ( ( ( uint32_t ) current_return & XWAR_OPCODE ) == XWAR_OPCODE ) ||
         ( ( ( uint32_t ) previous_return & XWAR_OPCODE ) == XWAR_OPCODE ) ) {
        xrn_print_all_warnings( ( uint32_t ) previous_return );
    }

    if ( ( ( uint32_t ) current_return & XERR_OPCODE ) == XERR_OPCODE ) {
        return XERR_GENERAL;

    } else {

        previous_return =
            ( int ) ( ( ( uint32_t ) previous_return & 0xfffff000 ) | ( uint32_t ) current_return );

    }

    return previous_return;
}

int xrn_convert_libtype_to_string( xrn_lib_xtype_t type, char *buff, int len
 )
{
    XPRINT_DEB(  );

    if ( buff == NULL ) {
        XPRINT_ERR( 2201, XERR_NULL_MSG );
        return XERR_NULL;
    }

    if ( len < strnlen( "binomial_commitment", 256 ) ) {
        XPRINT_ERR( 2202, XERR_BUFF_MSG );
        return XERR_BUFF;
    }

    if ( c_genesis == type ) {
        strcpy( buff, "genesis_base" );
    } else if ( type == c_base ) {
        strcpy( buff, "base" );
    } else if ( type == c_vector ) {
        strcpy( buff, "vector" );
    } else if ( type == c_matrix ) {
        strcpy( buff, "matrix" );
    } else if ( type == c_matrix_compressed ) {
        strcpy( buff, "matrix_compressed" );
    } else if ( type == c_start ) {
        strcpy( buff, "start" );
    } else if ( type == c_point ) {
        strcpy( buff, "point" );
    } else if ( type == c_monovalent_key ) {
        strcpy( buff, "monovalent_key" );
    } else if ( type == c_polyvalent_proof ) {
        strcpy( buff, "polyvalent_proof" );
    } else if ( type == c_polyvalent_key ) {
        strcpy( buff, "polyvalent_key" );
    } else if ( type == c_monomial_key ) {
        strcpy( buff, "monomial_key" );
    } else if ( type == c_monomial_commitment ) {
        strcpy( buff, "monomial_commitment" );
    } else if ( type == c_monomial_response ) {
        strcpy( buff, "monomial_response" );
    } else if ( type == c_monomial_proof ) {
        strcpy( buff, "monomial_proof" );
    } else if ( type == c_binomial_key ) {
        strcpy( buff, "binomial_key" );
    } else if ( type == c_binomial_commitment ) {
        strcpy( buff, "binomial_commitment" );
    } else if ( type == c_hash ) {
        strcpy( buff, "hash" );
    } else if ( type == c_signature_sym ) {
        strcpy( buff, "signature_sym" );
    } else if ( type == c_signature_asy ) {
        strcpy( buff, "signature_asy" );
    } else if ( type == c_ring_signature ) {
        strcpy( buff, "ring_signature" );
    } else if ( type == c_certificate_sym ) {
        strcpy( buff, "certificate_sym" );
    } else if ( type == c_certificate_asy ) {
        strcpy( buff, "certificate_asy" );
    } else if ( type == c_sym_encryption ) {
        strcpy( buff, "sym_encryption" );
    } else if ( type == c_asy_encryption ) {
        strcpy( buff, "asy_encryption" );
    } else if ( type == c_challenge ) {
        strcpy( buff, "challenge" );
    } else if ( type == c_solution ) {
        strcpy( buff, "solution" );
    } else {
        XPRINT_ERR( 2203, XERR_TYPE_MSG );
        return XERR_TYPE;
    }

    return XSUCCESS;
}

int xrn_convert_string_to_libtype( char *buff, xrn_lib_xtype_t * type
 )
{
    XPRINT_DEB(  );
    size_t len;

    if ( ( buff == NULL ) || ( type == NULL ) ) {
        XPRINT_ERR( 2204, XERR_NULL_MSG );
        return XERR_NULL;
    }

    len = strnlen( "binomial_commitment", 256 );

    if ( !strncmp( buff, "genesis", len ) ) {
        *type = c_genesis;
    } else if ( !strncmp( buff, "base", len ) ) {
        *type = c_base;
    } else if ( !strncmp( buff, "vector", len ) ) {
        *type = c_vector;
    } else if ( !strncmp( buff, "matrix", len ) ) {
        *type = c_matrix;
    } else if ( !strncmp( buff, "matrix_compressed", len ) ) {
        *type = c_matrix_compressed;
    } else if ( !strncmp( buff, "point", len ) ) {
        *type = c_point;
    } else if ( !strncmp( buff, "start", len ) ) {
        *type = c_start;
    } else if ( !strncmp( buff, "monomial_proof", len ) ) {
        *type = c_monomial_proof;
    } else if ( !strncmp( buff, "monomial_response", len ) ) {
        *type = c_monomial_response;
    } else if ( !strncmp( buff, "monomial_commitment", len ) ) {
        *type = c_monomial_commitment;
    } else if ( !strncmp( buff, "polyvalent_proof", len ) ) {
        *type = c_polyvalent_proof;
    } else if ( !strncmp( buff, "polyvalent_key", len ) ) {
        *type = c_polyvalent_key;
    } else if ( !strncmp( buff, "monovalent_key", len ) ) {
        *type = c_monovalent_key;
    } else if ( !strncmp( buff, "monomial_key", len ) ) {
        *type = c_monomial_key;
    } else if ( !strncmp( buff, "binomial_key", len ) ) {
        *type = c_binomial_key;
    } else if ( !strncmp( buff, "binomial_commitment", len ) ) {
        *type = c_binomial_commitment;
    } else if ( !strncmp( buff, "hash", len ) ) {
        *type = c_hash;
    } else if ( !strncmp( buff, "signature_sym", len ) ) {
        *type = c_signature_sym;
    } else if ( !strncmp( buff, "signature_asy", len ) ) {
        *type = c_signature_asy;
    } else if ( !strncmp( buff, "ring_signature", len ) ) {
        *type = c_ring_signature;
    } else if ( !strncmp( buff, "certificate_sym", len ) ) {
        *type = c_certificate_sym;
    } else if ( !strncmp( buff, "certificate_asy", len ) ) {
        *type = c_certificate_asy;
    } else if ( !strncmp( buff, "sym_encryption", len ) ) {
        *type = c_sym_encryption;
    } else if ( !strncmp( buff, "asy_encryption", len ) ) {
        *type = c_asy_encryption;
    } else if ( !strncmp( buff, "challenge", len ) ) {
        *type = c_challenge;
    } else if ( !strncmp( buff, "solution", len ) ) {
        *type = c_solution;
    } else {
        XPRINT_ERR( 2205, XERR_TYPE_MSG );
        return XERR_TYPE;
    }

    return XSUCCESS;

}

void xrn_set_default_log(
 )
{
    xlog.err_fp = XSTD_ERROR_OUTPUT;
    xlog.war_fp = XSTD_WARNING_OUTPUT;
    xlog.not_fp = XSTD_NOTIFICATION_OUTPUT;
    xlog.deb_fp = XSTD_DEBUG_OUTPUT;
    xlog.err_en = 1;
    xlog.war_en = 1;
    xlog.not_en = 0;
    xlog.deb_en = 0;
    xlog.deb_seed = ( uint32_t ) rand(  );
    xlog.war_shown = 0;
}

int xrn_change_error_log( FILE * fp, uint8_t err_en
 )
{
    if ( ( fp == NULL ) && ( err_en == 1 ) ) {
        XPRINT_ERR( 2206, XERR_NULL_MSG );
        return XERR_NULL;
    }

    xlog.err_en = err_en;
    xlog.err_fp = fp;
    return XSUCCESS;
}

int xrn_change_warning_log( FILE * fp, uint8_t war_en
 )
{
    if ( ( fp == NULL ) && ( war_en == 1 ) ) {
        XPRINT_ERR( 2207, XERR_NULL_MSG );
        return XERR_NULL;
    }

    xlog.war_en = war_en;
    xlog.war_fp = fp;
    xlog.war_shown = 0;
    return XSUCCESS;
}

int xrn_change_notification_log( FILE * fp, uint8_t not_en
 )
{
    if ( ( fp == NULL ) && ( not_en == 1 ) ) {
        XPRINT_ERR( 2208, XERR_NULL_MSG );
        return XERR_NULL;
    }

    xlog.not_en = not_en;
    xlog.not_fp = fp;
    return XSUCCESS;
}

int xrn_change_debug_log( FILE * fp, uint8_t deb_en, uint32_t deb_seed
 )
{
    if ( ( fp == NULL ) && ( deb_en == 1 ) ) {
        XPRINT_ERR( 2209, XERR_NULL_MSG );
        return XERR_NULL;
    }

    xlog.deb_en = deb_en;
    xlog.deb_seed = deb_seed;
    xlog.deb_fp = fp;
    return XSUCCESS;
}

int xrn_check_valid_type( xrn_lib_xtype_t type
 )
{
    XPRINT_DEB(  );

    int valid;

    valid = 0;

    if ( ( type == c_genesis ) || ( type == c_base ) || ( type == c_vector ) || ( type == c_matrix )
         || ( type == c_matrix_compressed ) || ( type == c_start ) || ( type == c_monomial_key )
         || ( type == c_binomial_key ) || ( type == c_polyvalent_proof ) || ( type == c_polyvalent_key )
         || ( type == c_monovalent_key ) || ( type == c_point ) || ( type == c_hash )
         || ( type == c_signature_sym ) || ( type == c_signature_asy ) || ( type == c_ring_signature )
         || ( type == c_certificate_sym ) || ( type == c_certificate_asy )
         || ( type == c_monomial_commitment ) || ( type == c_binomial_commitment )
         || ( type == c_monomial_response ) || ( type == c_monomial_proof )
         || ( type == c_sym_encryption ) || ( type == c_asy_encryption )
         || ( type == c_challenge )
         || ( type == c_solution ) ) {
        valid = 1;
    }

    if ( valid == 0 ) {
        return XERR_TYPE;
    }

    return XSUCCESS;
}

int xrn_check_complete_blocks( FILE * fpi
 )
{
    XPRINT_DEB(  );

    // Read the magic number from the input file
    header_block_t header;
    uint8_t crc8, crc8_stored;

    if ( fpi == NULL ) {
        XPRINT_ERR( 20, XERR_NULL_MSG );
        return XERR_NULL;
    }

    if ( 1 != fread( &header, sizeof( header ), 1, fpi ) ) {
        XPRINT_ERR( 16, XERR_IO_MSG );
        return XERR_IO;
    }

    if ( header.magic_number != XRN_MAGIC_NUMBER_BLOCK ) {
        XPRINT_ERR( 9, XERR_MAGIC_MSG );
        return XERR_MAGIC;
    }

    if ( header.offset < 0 || header.offset >= header.total_length ) {
        XPRINT_ERR( 8, XERR_IO_MSG );
        return XERR_IO;
    }

    if ( header.length == 0 || ( header.offset + header.length ) > header.total_length ) {
        XPRINT_ERR( 7, XERR_IO_MSG );
        return XERR_IO;
    }

    crc8_stored = header.crc8;
    header.crc8 = 0xff;
    crc8 = 0xff;
    xrn_crc8( ( uint8_t * ) & header, sizeof( header ) - 1, &crc8 );

    if ( crc8_stored != crc8 ) {
        XPRINT_ERR( 24, XERR_CRC_MSG );
        return XERR_CRC;
    }

    if ( ( header.offset == 0 ) && ( header.total_length == header.length ) ) {
        XPRINT_NOT( 0 );
        return XCOMPLETE;
    }

    XPRINT_NOT( 1 );
    return XNCOMPLETE;

}

int xrn_make_block( FILE * fpi, FILE * fpo, uint32_t tag, uint64_t offset, uint64_t length
 )
{
    XPRINT_DEB(  );

    uint8_t buffer[4096];
    size_t bytes_read;
    size_t bytes_to_write;
    uint32_t crc;
    uint8_t crc8;
    int i;
    header_block_t header;

    if ( ( fpi == NULL ) || ( fpo == NULL ) ) {
        XPRINT_ERR( 14, XERR_NULL_MSG );
        return XERR_NULL;
    }

    crc = 0xffffffff;

    // Determine the file size
    if ( 0 != fseek( fpi, 0, SEEK_END ) ) {
        XPRINT_ERR( 13, XERR_IO_MSG );
        return XERR_IO;
    }

    for ( i = 0; i < sizeof( header ); i++ ) {
        ( ( uint8_t * ) & header )[i] = 0;
    }

    header.total_length = ( uint64_t ) ftell( fpi );

    if ( 0 != fseek( fpi, 0, SEEK_SET ) ) {
        XPRINT_ERR( 19, XERR_IO_MSG );
        return XERR_IO;
    }

    // Validate the offset and length
    if ( ( length < 0 ) || ( offset < 0 ) || ( offset + length > header.total_length ) ) {
        XPRINT_ERR( 93, XERR_IO_MSG );
        return XERR_IO;
    }

    // Write the magic number to the output file

    header.magic_number = XRN_MAGIC_NUMBER_BLOCK;
    header.tag = tag;
    header.offset = offset;
    header.length = length;
    header.crc8 = 0xff;
    crc8 = 0xff;
    xrn_crc8( ( uint8_t * ) & header, sizeof( header ) - 1, &crc8 );
    header.crc8 = crc8;

    if ( 1 != fwrite( &header, sizeof( header ), 1, fpo ) ) {
        XPRINT_ERR( 10, XERR_IO_MSG );
        return XERR_IO;
    }

    // Seek to the specified offset in the input file
    if ( 0 != fseek( fpi, ( long int ) offset, SEEK_SET ) ) {
        XPRINT_ERR( 11, XERR_IO_MSG );
        return XERR_IO;
    }

    // Read and write the specified length of data from the input file to the output file
    while ( ( bytes_read = fread( buffer, sizeof( uint8_t ), sizeof( buffer ), fpi ) ) > 0 && length > 0 ) {
        bytes_to_write = ( bytes_read < length ) ? bytes_read : length;
        if ( bytes_to_write != fwrite( buffer, sizeof( uint8_t ), bytes_to_write, fpo ) ) {
            XPRINT_ERR( 12, XERR_IO_MSG );
            return XERR_IO;
        }

        length -= bytes_to_write;
        // calculate crc
        xrn_crc32( buffer, ( int ) bytes_to_write, &crc );
    }

    // Write the CRC to the end of the output file
    if ( 1 != fwrite( &crc, sizeof( uint32_t ), 1, fpo ) ) {
        XPRINT_ERR( 15, XERR_IO_MSG );
        return XERR_IO;
    }

    return XSUCCESS;
}

int xrn_strip_header_from_block( FILE * fpi, FILE * fpo
 )
{
    XPRINT_DEB(  );

    // Read the magic number from the input file
    uint32_t crc, crc_stored;
    uint8_t crc8, crc8_stored;
    uint8_t c;
    int i;

    header_block_t header;

    if ( ( fpi == NULL ) || ( fpo == NULL ) ) {
        XPRINT_ERR( 39, XERR_NULL_MSG );
        return XERR_NULL;
    }

    crc = 0xffffffff;

    if ( 1 != fread( &header, sizeof( header ), 1, fpi ) ) {
        XPRINT_ERR( 21, XERR_IO_MSG );
        return XERR_IO;
    }

    crc8_stored = header.crc8;
    header.crc8 = 0xff;
    crc8 = 0xff;
    xrn_crc8( ( uint8_t * ) & header, sizeof( header ) - 1, &crc8 );
    if ( crc8 != crc8_stored ) {
        XPRINT_ERR( 25, XERR_CRC_MSG );
        return XERR_CRC;
    }

    if ( header.magic_number != XRN_MAGIC_NUMBER_BLOCK ) {
        XPRINT_ERR( 38, XERR_MAGIC_MSG );
        return XERR_MAGIC;
    }

    if ( header.offset < 0 || header.offset >= header.total_length ) {
        XPRINT_ERR( 75, XERR_IO_MSG );
        return XERR_IO;
    }

    if ( header.length == 0 || ( header.offset + header.length ) > header.total_length ) {
        XPRINT_ERR( 76, XERR_IO_MSG );
        return XERR_IO;
    }

    // Read and write the specified length of data from the input file to the output file
    for ( i = 0; i < header.length; i++ ) {

        if ( 1 != fread( &c, sizeof( c ), 1, fpi ) ) {
            XPRINT_ERR( 18, XERR_IO_MSG );
            return XERR_IO;
        }

        if ( 1 != fwrite( &c, sizeof( c ), 1, fpo ) ) {
            XPRINT_ERR( 42, XERR_IO_MSG );
            return XERR_IO;
        }

        xrn_crc32( &c, sizeof( c ), &crc );

    }

    if ( 1 != fread( &crc_stored, sizeof( crc_stored ), 1, fpi ) ) {
        XPRINT_ERR( 43, XERR_IO_MSG );
        return XERR_IO;
    }

    if ( crc != crc_stored ) {
        XPRINT_ERR( 44, XERR_CRC_MSG );
        return XERR_CRC;
    }

    return XSUCCESS;
}

int xrn_join_blocks( FILE * in_file[XRN_MAX_MERGE_PARALLELISM], uint8_t number_of_inputs, FILE * out_file
 )
{
    XPRINT_DEB(  );

    uint32_t crc_stored32[XRN_MAX_MERGE_PARALLELISM], crc32[XRN_MAX_MERGE_PARALLELISM], crc_output32;
    header_block_t headero, header[XRN_MAX_MERGE_PARALLELISM];
    uint64_t sorted_indexes[XRN_MAX_MERGE_PARALLELISM], sorted_offsets[XRN_MAX_MERGE_PARALLELISM], swap;
    uint64_t covered_length, i, j, outc;
    uint64_t stream_index, sorted_stream_index;
    uint8_t c, c2, first_char;
    uint8_t crc8[XRN_MAX_MERGE_PARALLELISM], crc8_stored[XRN_MAX_MERGE_PARALLELISM];

    if ( ( in_file == NULL ) || ( out_file == NULL ) ) {
        XPRINT_ERR( 70, XERR_NULL_MSG );
        return XERR_NULL;
    }

    if ( number_of_inputs < 2 ) {
        XPRINT_ERR( 69, XERR_NULL_MSG );
        return XERR_NULL;
    }

    for ( stream_index = 0; stream_index < number_of_inputs; stream_index++ ) {
        if ( in_file[stream_index] == NULL ) {
            XPRINT_ERR( 68, XERR_NULL_MSG );
            return XERR_NULL;
        }

    }

    sorted_indexes[0] = 0;
    crc32[0] = 0xffffffff;
    for ( i = 1; i < XRN_MAX_MERGE_PARALLELISM; i++ ) {
        sorted_indexes[i] = i;
        crc32[i] = 0xffffffff;

    }

    // initialize outputs

    crc_output32 = 0xffffffff;
    for ( i = 0; i < sizeof( header[0] ); i++ ) {
        ( ( uint8_t * ) & headero )[i] = 0;
    }

    // initialize inputs

    for ( stream_index = 0; stream_index < number_of_inputs; stream_index++ ) {

        if ( 1 != fread( &header[stream_index], sizeof( header[stream_index] ), 1, in_file[stream_index] ) ) {
            XPRINT_ERR( 46, XERR_IO_MSG );
            return XERR_IO;
        }

        // check header

        crc8_stored[stream_index] = header[stream_index].crc8;
        header[stream_index].crc8 = 0xff;
        crc8[stream_index] = 0xff;
        xrn_crc8( ( uint8_t * ) & ( header[stream_index] ), sizeof( header[stream_index] ) - 1,
                  &( crc8[stream_index] ) );
        if ( crc8[stream_index] != crc8_stored[stream_index] ) {
            XPRINT_ERR( 64, XERR_CRC_MSG );
            return XERR_CRC;
        }

        // Validate the magic numbers

        if ( header[stream_index].magic_number != XRN_MAGIC_NUMBER_BLOCK ) {
            XPRINT_ERR( 63, XERR_MAGIC_MSG );
            return XERR_MAGIC;
        }

        if ( stream_index > 0 ) {

            // Validate total length
            if ( header[stream_index].total_length != header[stream_index - 1].total_length ) {
                XPRINT_ERR( 62, XERR_IO_MSG );
                return XERR_IO;
            }

            // Validate tag
            if ( header[stream_index].tag != header[stream_index - 1].tag ) {
                XPRINT_ERR( 61, XERR_IO_MSG );
                return XERR_IO;
            }

        }

    }

    // bouble sort by offset

    for ( i = 0; i < number_of_inputs; i++ ) {
        sorted_offsets[i] = header[i].offset;
        sorted_indexes[i] = i;
    }

    for ( i = 0; i < number_of_inputs - 1; i++ ) {
        for ( j = 0; j < number_of_inputs - 1 - ( int ) i; j++ ) {

            if ( sorted_offsets[j] > sorted_offsets[j + 1] ) {
                swap = sorted_offsets[j];
                sorted_offsets[j] = sorted_offsets[j + 1];
                sorted_offsets[j + 1] = swap;

                swap = sorted_indexes[j];
                sorted_indexes[j] = sorted_indexes[j + 1];
                sorted_indexes[j + 1] = swap;

            }

        }

    }

    // check if there are any empty spaces between contiguous blocks

    stream_index = 0;
    sorted_stream_index = sorted_indexes[stream_index];
    covered_length = header[sorted_stream_index].offset + header[sorted_stream_index].length;

    for ( stream_index = 1; stream_index < number_of_inputs; stream_index++ ) {

        sorted_stream_index = sorted_indexes[stream_index];

        // error empty space
        if ( header[sorted_stream_index].offset > covered_length ) {
            XPRINT_ERR( 48, XERR_IO_MSG );
            return XERR_IO;
        } else {
            if ( covered_length < header[sorted_stream_index].offset + header[sorted_stream_index].length ) {
                covered_length = header[sorted_stream_index].offset + header[sorted_stream_index].length;
            }

        }

    }

    // initialize output header

    headero.magic_number = XRN_MAGIC_NUMBER_BLOCK;
    headero.total_length = header[0].total_length;
    headero.tag = header[0].tag;

    stream_index = 0;
    sorted_stream_index = sorted_indexes[stream_index];
    headero.length = covered_length - header[sorted_stream_index].offset;
    headero.offset = header[sorted_stream_index].offset;

    crc8[0] = 0xff;
    headero.crc8 = 0xff;
    xrn_crc8( ( uint8_t * ) & headero, sizeof( headero ) - 1, &crc8[0] );
    headero.crc8 = crc8[0];

    if ( 1 != fwrite( &headero, sizeof( headero ), 1, out_file ) ) {
        XPRINT_ERR( 781, XERR_IO_MSG );
        return XERR_IO;
    }

    // for each element in the output stream
    for ( outc = headero.offset; outc < headero.offset + headero.length; outc++ ) {

        first_char = 0;
        for ( stream_index = 0; stream_index < number_of_inputs; stream_index++ ) {

            // sorted_stream_index = sorted_indexes[stream_index];
            sorted_stream_index = stream_index;

            // if character is contained in the stream
            if ( ( outc >= header[sorted_stream_index].offset )
                 && ( outc < header[sorted_stream_index].length + header[sorted_stream_index].offset ) ) {

                // save the first character
                if ( first_char == 0 ) {

                    if ( 1 != fread( &c, sizeof( c ), 1, in_file[sorted_stream_index] ) ) {
                        XPRINT_ERR( 31, XERR_IO_MSG );
                        return XERR_IO;
                    }

                    xrn_crc32( &c, sizeof( c ), &crc32[sorted_stream_index] );

                    first_char = 1;

                    // check if the character is consistent for all the input streams
                } else {

                    if ( 1 != fread( &c2, sizeof( c2 ), 1, in_file[sorted_stream_index] ) ) {
                        XPRINT_ERR( 32, XERR_IO_MSG );
                        return XERR_IO;
                    }

                    if ( c == c2 ) {
                        xrn_crc32( &c, sizeof( c ), &crc32[sorted_stream_index] );
                    } else {
                        XPRINT_ERR( 47, XERR_IO_MSG );
                        return XERR_IO;
                    }

                }

            }

        }

        xrn_crc32( &c, sizeof( c ), &crc_output32 );

        if ( 1 != fwrite( &c, sizeof( c ), 1, out_file ) ) {
            XPRINT_ERR( 782, XERR_IO_MSG );
            return XERR_IO;
        }

    }

    // check all the input crcs

    for ( stream_index = 0; stream_index < number_of_inputs; stream_index++ ) {

        // sorted_stream_index = sorted_indexes[stream_index];
        sorted_stream_index = stream_index;
        if ( 1 !=
             fread( &crc_stored32[sorted_stream_index], sizeof( crc_stored32[sorted_stream_index] ), 1,
                    in_file[sorted_stream_index] ) ) {
            XPRINT_ERR( 41, XERR_IO_MSG );
            return XERR_IO;
        }

        if ( crc32[sorted_stream_index] != crc_stored32[sorted_stream_index] ) {
            XPRINT_ERR( 45, XERR_CRC_MSG );
            return XERR_CRC;
        }

    }

    // write output crc

    if ( 1 != fwrite( &crc_output32, sizeof( crc_output32 ), 1, out_file ) ) {
        XPRINT_ERR( 65, XERR_IO_MSG );
        return XERR_IO;
    }

    return XSUCCESS;

}

